package de.gameduell.framework.queue {
	import de.gameduell.framework.debug.Debug;
	import de.gameduell.framework.threads.AllBlockingThread;
	import de.gameduell.framework.threads.BlockingActionThread;
	import de.gameduell.framework.threads.ThreadPoolManager;
	import de.gameduell.framework.threads.StartThreadRequest;
	import de.gameduell.framework.util.LockManager;	

	/**
	 * @author ddoe
	 */
	public class GDActionQueue {

		// action queue id's
		public static const MAIN_QUEUE:String = ThreadPoolManager.MAIN_QUEUE;
		public static const POPUP_QUEUE:String = ThreadPoolManager.POPUP_QUEUE;
		public static const CHAT_QUEUE:String = ThreadPoolManager.CHAT_QUEUE;
		public static const INTERNAL_ALL_BLOCKING_QUEUE:String = ThreadPoolManager.INTERNAL_ALL_BLOCKING_QUEUE; 
		
		// singleton instance
		private static var INSTANCE:GDActionQueue = null;
		
		private var actions:Array;
		private var lockMgr:LockManager;
		
		// multi queue support 
		private var threadPoolManager:ThreadPoolManager;
		private var registeredQueueIds:Array;
		
		public static function getInstance():GDActionQueue{
			if(INSTANCE == null){
				INSTANCE = new GDActionQueue();		
			}
			return INSTANCE;
		}
		
		public function GDActionQueue() {
			actions = new Array();
			lockMgr = LockManager.getInstance();
		}
		
		public function add(action:IGDAction, queueId:String = null):void {
			
			// parallel multi-queue support enabled?
			if (threadPoolManager != null) {
				
				// don't allow manual usage of internal all blocking queue
				if (queueId == INTERNAL_ALL_BLOCKING_QUEUE) {
					throw new Error("Queue INTERNAL_ALL_BLOCKING_QUEUE is reserved for internal usage only, use any other queueId!"); 
				}

				// wrap action as thread
				var wrappedActionThread:BlockingActionThread = new BlockingActionThread(action);

				// synchronize all queues ?
				if (queueId == null) {
					
					// wrap the wrapper :)
					var allBlockingThread:AllBlockingThread = new AllBlockingThread(threadPoolManager, registeredQueueIds, wrappedActionThread);
					
					// let handle by thread pool manager, start blocking on main queue
					threadPoolManager.handleEvent(new StartThreadRequest(MAIN_QUEUE, allBlockingThread));
					
				}
				// synchronize only the target queue ?
				else {
					// let handle by thread pool manager
					threadPoolManager.handleEvent(new StartThreadRequest(queueId, wrappedActionThread));
				}
				
			}
			// only one global queue ?
			else {
				lockMgr.lock(this);	
				actions.push(action);	
				lockMgr.unlock(this);
				
				if(actions.length == 1){
					action.addEventListener(ActionEvent.DONE, onActionDone);		
					action.compute();			
				}	
			}
		}
		
		public function getQueuedActionDescriptions():String {
		
			// parallel multi-queue support enabled?
			if (threadPoolManager != null) {
				return threadPoolManager.getPoolDump();
			}
			// only one global queue ?
			else {
				var descriptions:String = "";
				for each (var action:IGDAction in actions){
					descriptions = descriptions+ "\n"+action.description;
				}
				return descriptions;
			}
		}
		
		public function onActionDone(event:ActionEvent = null):void{
			var action:IGDAction = actions[0];
			action.removeEventListener(ActionEvent.DONE, onActionDone);		
			actions.shift();
			if(actions.length > 0){
				action = actions[0];
				action.addEventListener(ActionEvent.DONE, onActionDone);		
				action.compute();
//				Debug.trace("GDActionQueue::compute action "+action.description);	
			}
		}
		
		// -- delegate / proxy to thread pool manager --
		
		public function registerThreadPoolManager(threadPoolManager:ThreadPoolManager, registeredQueueIds:Array):void {
			this.threadPoolManager = threadPoolManager;
			this.registeredQueueIds = registeredQueueIds;
			
			// required, add main queue if not contained yet
			if (registeredQueueIds.indexOf(MAIN_QUEUE) < 0) {
				registeredQueueIds.push(MAIN_QUEUE);
			}
			
			// required, add internal queue if not contained yet
			if (registeredQueueIds.indexOf(INTERNAL_ALL_BLOCKING_QUEUE) < 0) {
				registeredQueueIds.push(INTERNAL_ALL_BLOCKING_QUEUE);
			}
						
		}
		
		public function unregisterThreadPoolManager():void {
			this.threadPoolManager = null;
			this.registeredQueueIds = null;
		}
		
		
	}
}
